#!/usr/bin/python
# -*- coding: utf-8 -*-

import time, redis, datetime, json, pymongo, hashlib, random, os, sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from exchange.utils.uuid import uuid
from exchange.com.db import db



'''
撤单处理
1、删除 orders 相应的记录
2、新增 queue 记录
3、恢复 balances 账户可用余额
'''
def match_for_cancel(queue):
    account, symbol, action, seqno = queue
    ex = db.mdb.exchange
    tb_orders = ex["orders.%s.%s" % (symbol, action)]
    tb_queue = ex.queue
    tb_history = ex["history." + symbol]
    tb_balances = ex.balances
    order = tb_orders.find_one({"seqno": seqno})
    if order:
        if order["account"] != account:
            print("账户错误")
            return
        if order["action"] != action:
            print("交易类型错误")
            return
        coin = symbol.split("/")[1] if action == "buy" else symbol.split("/")[0]
        balances = tb_balances.find_one({"account": account, "coin": coin})
        if not balances:
            print("账户余额错误")
            return
        # insert queue
        tb_queue.insert_one({
            "seqno": order["seqno"],
            "account": account,
            "action": action,
            "type": "cancel",
            "symbol": symbol,
            "price": order["price"],
            "amount": order["amount"]
        })
        session = db.mdb.start_session()
        session.start_transaction()
        try:
            # remove orders
            tb_orders.remove({"seqno": seqno})
            # update balances
            tb_balances.update_one({"account": account, "coin": coin}, {"$inc": {"balances": order["amount"]}})
        except:
            session.abort_transaction()
        else:
            session.commit_transaction()
        finally:
            session.end_session()



'''
执行交易
1、查询用户的可用额度是否满足交易的需求
2、买入时、从卖出挂单中查询价格合适的进行抵消
3、卖出时、从买入挂单中查询价格合适的进行抵消
4、未成交的部分、加入到挂单中
'''
def match_for_order(queue):
    account, symbol, action, price, amount = queue
    price = float(price)
    amount = float(amount)
    seqno = '%d' % uuid.get_tick()

    if action == 'buy' and price <= 0.0:
        print("买入时价格不能小于等于 0.0")
        print(queue)
        return

    ex = db.mdb.exchange
    tb_orders = ex["orders.%s.%s" % (symbol, action)]
    tb_queue = ex.queue
    tb_history = ex["history." + symbol]
    tb_balances = ex.balances

    coin = symbol.split("/")[1] if action == "buy" else symbol.split("/")[0]
    # balances = tb_balances.find_one({"account": account, "coin": coin})
    # 查询用户余额状态
    # if not balances or balances["balances"] < amount:
    #     print("账户余额不足", queue)
    #     return

    # insert queue
    tb_queue.insert_one({
        "seqno": seqno,
        "account": account,
        "action": action,
        "type": "order",
        "symbol": symbol,
        "price": price,
        "amount": amount
    })

    # 插入挂单数据
    tb_orders.insert_one({
        "seqno": seqno,
        "account": account,
        "price": price,
        "amount": amount,
        "opened": amount
    })

    # 更新账户余额
    tb_balances.update_one({"account": account, "coin": coin}, {"$inc": {"balances": -amount}})

    deal(action, symbol)


def deal(action, symbol):
    ex = db.mdb.exchange
    tb_queue = ex.queue
    tb_history = ex["history." + symbol]

    # 抵消挂单数据
    over = False
    while not over:
        orders_buy  = ex["orders.%s.%s" % (symbol, "buy" )]
        orders_sell = ex["orders.%s.%s" % (symbol, "sell" )]
        buys    = [o for o in  orders_buy.find().sort([("price", -1), ("seqno", 1)]).limit(5)]
        sells   = [o for o in orders_sell.find().sort([("price",  1), ("seqno", 1)]).limit(5)]
        if len(buys) == 0 or len(sells) == 0:
            over = True
            break
        for b in buys:
            if over:
                break
            for s in sells:
                if b["price"] < s["price"]:
                    over = True
                    break
                deal_price = b["price"] if b["seqno"] < s["seqno"] else s["price"]
                deal_amount = b["opened"] if b["opened"] < s["opened"] else s["opened"]

                if deal_amount == 0.0:
                    break

                b["opened"] -= deal_amount
                s["opened"] -= deal_amount

                tb_history.insert_one({
                    "buy_seqno": b["seqno"], "sell_seqno": s["seqno"],
                    "buyer": b["account"], "seller": s["account"],
                    "price": deal_price, "amount": deal_amount,
                    "action": action
                })

                print({
                    "buy_seqno": b["seqno"], "sell_seqno": s["seqno"],
                    "buyer": b["account"], "seller": s["account"],
                    "price": deal_price, "amount": deal_amount,
                    "action": action
                })

                orders_buy.update_one({"seqno": b["seqno"]}, {"$inc": {"opened": -deal_amount}})
                orders_sell.update_one({"seqno": s["seqno"]}, {"$inc": {"opened": -deal_amount}})

                orders_buy.remove({"opened": 0})
                orders_sell.remove({"opened": 0})


#
# 撮合交易
#
def match():

    print("match servcie is started.")

    while 1:

        q = db.rdb.rpop("queue")
        if not q:
            time.sleep(0.1)
            continue

        queue = q.split(",")

        # 撤单处理
        if queue[0] == "cancel":
            match_for_cancel(queue[1:])
            continue

        # 下单处理
        if queue[0] == "order":
            match_for_order(queue[1:])
            continue



if __name__ == '__main__':
    match()
